#include <ClanLib/display.h>
#include "App.h"
#include "AudioEngine/SineProducer.h"
#include "AudioEngine/VolumeEnvelope.h"
#include "EqualTemperament.h"

void App::start(const std::vector<CL_String> &args)
{
	quit = false;

	try
	{
		CL_DisplayWindow window("MusicLife", 640, 480);
		CL_Slot slots[] = {
			window.sig_window_close().connect(this, &App::on_window_close),
			window.get_ic().get_keyboard().sig_key_up().connect(this, &App::on_input_up),
			window.get_ic().get_keyboard().sig_key_down().connect(this, &App::on_input_down)
		};
		CL_GraphicContext gc = window.get_gc();

		while (!quit)
		{
			gc.clear(CL_Colorf(0.0f,0.0f,0.0f));
			window.flip();
			//CL_Console::write_line("%1", playSessions.size());

			CL_KeepAlive::process();
		}
	}
	catch(CL_Exception &exception)
	{
		CL_Console::write_line("Exception caught: " + exception.get_message_and_stack_trace());
		CL_Console::wait_for_key();
	}
}

class NoSuchNote{};

static int noteFromKey(CL_InputEvent const& key)
{
	int rightbracket = 221, leftbracket = 219, slash = 191, dot = 190,
		comma = 188, equals = 187, semicolon = 186;

	std::map<int, int> notes;
	notes['Z'] = 0; notes['S'] = 1; notes['X'] = 2; notes['D'] = 3; notes['C'] = 4; notes['V'] = 5;
	notes['G'] = 6; notes['B'] = 7; notes['H'] = 8; notes['N'] = 9; notes['J'] = 10; notes['M'] = 11;
	notes[comma] = 12; notes['L'] = 13; notes[dot] = 14; notes[semicolon] = 15; notes[slash] = 16;
	notes['Q'] = 12; notes['2'] = 13; notes['W'] = 14; notes['3'] = 15; notes['E'] = 16; notes['R'] = 17;
	notes['5'] = 18; notes['T'] = 19; notes['6'] = 20; notes['Y'] = 21; notes['7'] = 22; notes['U'] = 23;
	notes['I'] = 24; notes['9'] = 25; notes['O'] = 26; notes['0'] = 27; notes['P'] = 28; notes[leftbracket] = 29;
	notes[equals] = 30; notes[rightbracket] = 31;
	if(notes.find(key.id) == notes.end()) throw NoSuchNote();
	return notes[key.id];
}

static double pitchFromID(CL_InputEvent const& key)
{
	EqualTemperament temperament;
	return temperament.get_frequency(440.0, noteFromKey(key) + 3); // +3 because 440 is the note A and we want C
}

void App::on_input_up(const CL_InputEvent &key, const CL_InputState &state)
{
	if(key.id == CL_KEY_ESCAPE)
	{
		quit = true;
	}

	try
	{
		noteFromKey(key);
	}
	catch(NoSuchNote const&)
	{
		return;
	}

	AudioEngine::Producer* producer = (AudioEngine::Producer*)
		playSessions[key.id]->get_producer().get(); // const hack... due to sp being crap
	producer->key_release();
	slotContainer.connect(producer->sig_finished, this, &App::on_session_finished, key.id);
}

void App::on_input_down(const CL_InputEvent &key, const CL_InputState &state)
{
	using namespace AudioEngine;
	if(key.repeat_count > 0) return;
	try
	{
		sp<Producer> sine = new SineProducer(pitchFromID(key));
		sp<Producer const> envelope = new VolumeEnvelope(sine, EnvelopeParameters(0, 400, .5, 1000));
		sp<PlaySession> session = new PlaySession(envelope);
		playSessions[key.id] = session;
	}
	catch(NoSuchNote const&)
	{
	}
}

void App::on_window_close()
{
	quit = true;
}

void App::on_session_finished(int keyID)
{
	playSessions.erase(keyID);
}
